/* Implements linked_list abstract data type. */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "linked_list.h"

typedef struct linked_list_link_data_t *linked_list_link_t;

/* Define link*/
struct linked_list_link_data_t {
  linked_list_element_t k;
  linked_list_element_t e;
  linked_list_link_t next;
};

struct linked_list_data_t {
  	linked_list_link_t head;
	linklist_key_cmp_fn_t key_cmp_func;
};

linked_list_t
linked_list_create(linklist_key_cmp_fn_t linklist_key_comparator_fn)
{
  	linked_list_t l = (linked_list_t)malloc(sizeof(struct linked_list_data_t));
	l->key_cmp_func = linklist_key_comparator_fn;
  	l->head = NULL;
  	return l;
}

/* Private */
static linked_list_link_t
linked_list_new_element(linked_list_element_t k,linked_list_element_t e)
{
  	linked_list_link_t llt = (linked_list_link_t)malloc(sizeof(struct linked_list_link_data_t));
  	llt->e  = e;
  	llt->k = k;
  	llt->next = NULL;
  	return llt;
}

int linked_list_destroy(linked_list_t l, linked_list_key_destroy_fn_t destroyKeyFunc, linked_list_value_destroy_fn_t destroyValFunc)
{
	if (l==NULL) return 0;
	linked_list_link_t cur = l->head;
	linked_list_link_t old_cur;
	while(cur){
		destroyKeyFunc(cur->k);
		destroyValFunc(cur->e);
		old_cur = cur;	
		cur = cur -> next;
		free(old_cur);
	}
	free(l);
	return 0;
}

int
linked_list_update_or_append(linked_list_t l, linked_list_element_t k,linked_list_element_t e) 
{
  	linked_list_link_t cur;	
	if (l == NULL) return 1;
  
  	cur = l->head;
  
  	/* when l is empty, we need to have it point the newly create link */
  	if (cur==NULL){
    		l->head = linked_list_new_element(k,e); 
    		return 0;
  	}else{
		while (cur) {
			if (l->key_cmp_func(cur->k,k)==0){
				free(cur->e);
				cur->e = e;
				return 0;
			}
	    	cur = cur->next;
	  	}
	  	cur->next = linked_list_new_element(k,e);
	}
	return 0;
}

boolean_t
linked_list_is_empty(linked_list_t l)
{
  assert(l != NULL);
  return (l->head == NULL);
}

linked_list_element_t
linked_list_key_search(linked_list_t l, linked_list_element_t k){
  linked_list_link_t cur;	
  assert(l != NULL);
  if (linked_list_is_empty(l))
    return NULL;

  cur = l->head;
  while(cur){
    if (l->key_cmp_func(cur->k,k)==0){
      return cur->e;
    }
    cur = cur->next;
  }
  return NULL;
}

boolean_t  
linked_list_apply(linked_list_t l, linked_list_pfapply_t pf, linked_list_pfapply_closure_t cl) 
{
  assert(l != NULL && pf != NULL);
  
  if (linked_list_is_empty(l))
    return FALSE;

  linked_list_link_t cur;
  for (cur = l->head; cur; cur = cur->next) {
    if (!pf(cur->k,cur->e, cl))
      break; 
  }
  return TRUE;
}


  
